<?PHP

/**
 *  Zebra PHP Framework
 *
 *  PHP MySQL Database Wrapper Class
 *
 *  This class provides a set of methods for working with MySQL databases.
 *  It's main goal is to act as a helping tool in the developement process by providing a full featured debugging interface.
 *
 *  See the documentation for more info.
 *
 *  Read the LICENSE file, provided with the package, to find out how you can use this PHP script.
 *
 *  If you don't find this file, please write an email to noname at nivelzero dot ro and you will be sent a copy of the license file
 *
 *  For more resources visit {@link http://stefangabos.blogspot.com}
 *
 *  @author     Stefan Gabos <ix@nivelzero.ro>
 *  @version    1.0.9 (last revision: May 30, 2007)
 *  @copyright  (c) 2006 - 2007 Stefan Gabos
 *  @package    database
 *  @example    example.php
 */

//error_reporting(E_ALL);

class database
{

    /**
     *  After an INSERT, UPDATE, DELETE query, by reading this property you get the number of affected rows
     *
     *  <b>This is a read-only property!</b>
     *
     *  @var    integer
     */
    var $affectedRows;

    /**
     *  A folder where to store cached results of queries (with trailing slash)
     *
     *  Default is "/cache" in the same folder as the class
     *
     *  <i>You should have a cron job cleaning up this folder of outdated files</i>
     *
     *  @var    string
     */
    var $cacheFolder;

    /**
     *  The currently selected MySQL database
     *
     *  Default is FALSE
     *
     *  @access private
     */
    var $database;

    /**
     *  Setting this property to TRUE, will instruct the class to gather debug information.
     *
     *  This information can later be shown by calling the  {@link show_debug_information()} method
     *
     *  Default is FALSE
     *
     *  @var    boolean
     */
    var $debug;

    /**
     *  An array of IP addresses for which, if called, the debug window will be shown
     *
     *  Leaving this an empty array, will display the debug window for everybody
     *
     *  Default is an empty array
     *
     *  @var    array
     */
     var $debuggerIP;

    /**
     *  Stores debug information
     *
     *  Default is an empty array
     *
     *  @access private
     */
    var $debugInfo;

    /**
     *  After a SELECT query, by reading this property you get the number of records that would've been returned if there was no LIMIT
     *  (of course, if there's a LIMIT applied to the query. if not, this property's value is equal to {@link returnedRows})
     *
     *  <b>This is a read-only property!</b>
     *
     *  @var    integer
     */
    var $foundRows;

    /**
     *  By setting this property to TRUE, the execution of the script will be halted upon an unsuccessful query and, if the {@link debug}
     *  property is set to TRUE and the viewer's IP address it's in the {@link debuggerIP} array, outputs the debug window
     *
     *  Default is TRUE
     *
     *  @var    boolean
     */
    var $haltOnErrors;

    /**
     *  Language file to use
     *
     *  The name of the php language file you wish to use from the /languages folder.
     *
     *  Without the extension! (i.e. "german" for the german language not "german.php")
     *
     *  Default is "english"
     *
     *  @var   string
     */
    var $language;

    /**
     *  MySQL link identifier
     *
     *  default is FALSE
     *
     *  @access private
     */
    var $link;

    /**
     *  The time (in seconds) after which a query will be considered to be running for too long
     *
     *  Default is 30
     *
     *  @var    integer
     */
    var $maxQueryTime;
    
    /**
     *  The email address to which a notification to be sent when a query's execution time exceeds {@link maxQueryTime}
     *
     *  Default is "root@localhost.com"
     *
     *  @var    string
     */
    var $maxQueryTimeExceeded_notificationAddress;
    
    /**
     *  The domain name to use in the subject of the mails sent when a query's execution time exceeds {@link maxQueryTime}
     *
     *  Default is "localhost.com"
     *
     *  @var    string
     */
    var $maxQueryTimeExceeded_notifierDomain;
    
    /**
     *  By setting this property to TRUE, a minimized version of the debug bar will be shown by default
     *
     *  Default is FALSE
     *
     *  @var    boolean
     */
    var $minimizeDebugger;

    /**
     *  After a SELECT query, by reading this property you get the number of returned records
     *
     *  <b>This is a read-only property!</b>
     *
     *  @var    integer
     */
    var $returnedRows;

    /**
     *  This sets how many of the first records returned by a SELECT query should be shown in the debug window
     *
     *  <i>Be aware of the fact that having this property set to a high number (thousands or more) and having a query that
     *  returns so many rows, it can cause your script to crash due to memory limitations. In this case you should either
     *  lower the value of this property or try and set php's memory limit higher</i>
     *
     *  <code>
     *      ini_set("memory_limit","20M");
     *  </code>
     *
     *  Default is 50
     *
     *  @since  1.0.9
     *
     *  @var    integer
     */
    var $showMaxRows;
    
    /**
     *  Default template folder to use
     *
     *  Note that only the folder of the template you wish to use needs to be specified. Inside the folder
     *  you <b>must</b> have the <b>debug.xtpl</b> file which will be automatically used
     *
     *  Default is "default"
     *
     *  @var    string
     */
    var $template;
    
    /**
     *  Constructor of the class
     *
     *  Creates a new instance of the class
     */
    function database()
    {
    
        // Sets default values of the class' properties
        // We need to do it this way for the variables to have default values PHP 4
        // public properties
        $this->maxQueryTime = 30;

        $this->maxQueryTimeExceeded_notificationAddress = "root@localhost.com";

        $this->maxQueryTimeExceeded_notifierDomain = "localhost.com";

        $this->debug = true;

        $this->debuggerIP = array();

        $this->haltOnErrors = true;

        $this->minimizeDebugger = false;

        $this->template = "default";
        
        $this->showMaxRows = 50;

        // private properties
        $this->database = false;

        $this->link = false;

        $this->debugInfo = array();

        // get path of class and replace (on a windows machine) \ with /
        // this path is to be used for all includes as it is an absolute path
        $this->classAbsolutePath = preg_replace("/\\\/", "/", dirname(__FILE__));
        
        // remove $_SERVER["DOCUMENT_ROOT"] from the path
        // this path is to be used from within HTML as it is a relative path
        $this->classRelativePath = preg_replace("/" . preg_replace("/\//", "\/", $_SERVER["DOCUMENT_ROOT"]) . "/i", "", $this->classAbsolutePath);
        
        $this->cacheFolder = $this->classAbsolutePath . "/cache/";
        
        // load the default language
        $this->setLanguage("english");
        
    }
    
    /**
     *  Checks if the connection to the MySQL server has been previously established by the connect() method
     *
     *  @access private
     */
    function _connected()
    {

        // if there's no connection to a mysql database
        if (!$this->link) {

            // return false
            return false;

        }

        // return true if there is no error
        return true;

    }

    /**
     *  Connects to a MySQL database
     *
     *  Example:
     *
     *  <code>
     *      /**
     *          notice that we're doing no error checking as we will have
     *          any errors show up in the debug window
     *          so don't forget to have at the end of your code a call to
     *          the show_debug_info() method
     *      {@*}
     *      $db->connect("localhost", "root", "", "test");
     *  </code>
     *
     *  @param  string  $MySQLHost      The address of the MySQL server to connect to (i.e. localhost)
     *
     *  @param  string  $MySQLUser      The user name used for authentication when connecting to the MySQL server
     *
     *  @param  string  $MySQLPassword  The password used for authentication when connecting to the MySQL server
     *
     *  @param  string  $MySQLDatabase  The database to be selected after connection is established
     *
     *  @param  boolean $newConnection  (optional) By default, if a second call is made to mysql_connect() with
     *                                  the same arguments, no new link will be established, but instead, the link
     *                                  identifier of the already opened link will be returned, so you <b>MUST</b> set
     *                                  this to TRUE whenever you instantiate this class other than for the first time
     *                                  inside the same script for accessing different databases!
     *
     *  @return boolean TRUE on success and FALSE upon failure
     */
    function connect($MySQLHost, $MySQLUser, $MySQLPassword, $MySQLDatabase, $newConnection = false)
    {
    
        // tries to connect to the mysql database using the given parameters
        $this->link = @mysql_connect($MySQLHost, $MySQLUser, $MySQLPassword, $newConnection);
        
        // if connection could not be established
        if (!$this->link) {
        
            // if debug is on
            if ($this->debug) {
            
                // save debug information
                $this->saveDebugInfo(
                
                    "messages",
                    
                    array(
                    
                        "message"   =>  "strLang_couldNotConnectToDatabase",
                        "method"    =>  "connect()"
                        
                    ),
                    
                    true
                    
                );
                
            }
            
            // return false
            return false;
            
        // if connection could be established
        } else {
        
            // select the database
            $this->database = @mysql_select_db($MySQLDatabase, $this->link);
            
            // if database could not be selected
            if (!$this->database) {
            
                // if debug is on
                if ($this->debug) {
                
                    // save debug information
                    $this->saveDebugInfo(
                    
                        "messages",
                        
                        array(
                        
                            "message"   =>  "strLang_couldNotSelectDatabase",
                            "method"    =>  "connect()",
                            
                        ),
                        
                        true
                        
                    );
                    
                }
                
                // return false
                return false;

            }
            
        }
        
        // return true if there is no error
        return true;
        
    }
    
    /**
     *  Counts the values in a column of a table
     *
     *  Example:
     *
     *  <code>
     *      /**
     *          notice that we're doing no error checking as we will have
     *          any errors show up in the debug window
     *          so don't forget to have at the end of your code a call to
     *          show_debug_info() method
     *      {@*}
     *      $countName = $db->dcount("name", "people", "gender = ?", array($_POST["gender"]));
     *  </code>
     *
     *  @param  string  $column         Name of the column in which to do the counting
     *
     *  @param  string  $table          Name of the table to use
     *
     *  @param  string  $condition      (optional) A standard MySQL WHERE condition
     *
     *                                  Default is ""
     *
     *  @param  array   $replacements   (optional) An array with as many items as the total "?" symbols in $column, $table and
     *                                  $condition. Each item will be automatically {@link escape()}-ed and will replace
     *                                  the corresponding "?"
     *
     *                                  Default is ""
     *
     *  @param  mixed   $cache          (optional) Instructs the script on whether it should cache the query's results. This argument
     *                                  can be either FALSE - meaning no cache - or an integer representing the number of seconds after
     *                                  which the cached results are considered to be expired
     *
     *                                  Default is FALSE
     *
     *  @param  boolean $highlight      (optional) Whether or not the query should be highlighted in the debugger window
     *
     *                                  Default is FALSE
     *
     *                                  This argument is available since version 1.0.7
     *
     *  @return mixed   The counted value
     */
    function dcount($column, $table, $condition = "", $replacements = "", $cache = false, $highlight = false)
    {

        // get the backtrace info so that the debug information will be about where this particular method was called from
        $this->backtraceInfo = debug_backtrace();

        // executes query
        $this->lastResult = $this->query("
        
            SELECT
                COUNT(" . $column . ") as countval
            FROM
                " . $table .
                
            ($condition != "" ? " WHERE " . $condition : "")

        ,$replacements, $cache, $highlight);

        // if query was executed successfully
        if ($this->lastResult && $this->foundRows > 0) {

            // get all the data in the row
            $row = $this->fetch_assoc();

            // return the result
            return $row["countval"];

        }

        // if error or no rows
        return 0;

    }
    
    /**
     *  Shorthand for DELETE queries
     *
     *  <i>I recommend the use of the {@link query()} method instead and writing the actual query for better code readability</i>
     *
     *  @param  string  $table          Table from which to delete
     *
     *  @param  string  $condition      (optional) A standard MySQL WHERE condition
     *
     *                                  Default is ""
     *
     *  @param  array   $replacements   (optional) An array with as many items as the total "?" symbols in $table and
     *                                  $condition. Each item will be automatically {@link escape()}-ed and will replace
     *                                  the corresponding "?"
     *
     *  @param  boolean $highlight      (optional) Whether or not the query should be highlighted in the debugger window
     *
     *                                  Default is FALSE
     *
     *  @since  1.0.9
     *
     *  @return boolean     Returns TRUE on success of FALSE on error
     */
    function delete($table, $condition = "", $replacements = "", $highlight = false)
    {
    
        // get the backtrace info so that the debug information will be about where this particular method was called from
        $this->backtraceInfo = debug_backtrace();

        // executes query
        $this->lastResult = $this->query("

            DELETE FROM
                " . $table .
                
            ($condition != "" ? " WHERE " . $condition : "")

        ,$replacements, false, $highlight);
        
        // if query was executed successfully
        if ($this->lastResult) {

            // returns true
            return true;

        }
        
        // return false on error
        return false;
    
    }

    /**
     *  Returns value/values from ONE row of a table
     *
     *  Example:
     *
     *  <code>
     *      /**
     *          notice that we're doing no error checking as we will have
     *          any errors show up in the debug window
     *          so don't forget to have at the end of your code a call to
     *          show_debug_info() method
     *      {@*}
     *      $foundData = $db->dlookup("name, surname, age", "people", "countryid = ?", array($_POST["countryID"]));
     *  </code>
     *
     *  @param  string  $field          One or more fields to return in the result. If only one field is specified, the
     *                                  returned result will be the specific field's value whereas if more fields are specified,
     *                                  the returned result will be an associative array.
     *
     *                                  <i>You can also use the "*" sign to return all the fields from a row</i>
     *
     *  @param  string  $table          Name of the table to use
     *
     *  @param  string  $condition      (optional) A standard MySQL WHERE condition
     *
     *                                  Default is ""
     *
     *  @param  array   $replacements   (optional) An array with as many items as the total "?" symbols in $field, $table and
     *                                  $condition. Each item will be automatically {@link escape()}-ed and will replace
     *                                  the corresponding "?"
     *
     *  @param  mixed   $cache          (optional) Instructs the script on whether it should cache the query's results. This argument
     *                                  can be either FALSE - meaning no cache - or an integer representing the number of seconds after
     *                                  which the cached results are considered to be expired
     *
     *                                  Default is FALSE
     *
     *  @param  boolean $highlight      (optional) Whether or not the query should be highlighted in the debugger window
     *
     *                                  Default is FALSE
     *
     *                                  this argument is available since version 1.0.7
     *
     *  @return mixed   value/values found or an empty string if nothing was found
     */
    function dlookup($field, $table, $condition = "", $replacements = "", $cache = false, $highlight = false)
    {

        // get the backtrace info so that the debug information will be about where this particular method was called from
        $this->backtraceInfo = debug_backtrace();

        // executes query
        $this->lastResult = $this->query("
        
            SELECT
                " . $field . "
            FROM
                " . $table .
                
            ($condition != "" ? " WHERE " . $condition : "")

        ,$replacements, $cache, $highlight);

        // if query was executed successfully and one or more results were found
        if ($this->lastResult && $this->foundRows > 0) {

            // take *only* the first row
            $row = $this->fetch_assoc();

            // if more fields were specified explode them to an array
            $fields_list = explode(",", $field);

            // if all fields were requested
            if (trim($field) == "*") {

                // return the whole row
                return $row;

            // if more than one field was requested
            } elseif (count($fields_list)>1) {

                $retData = array();

                // iterate through the requested fields
                foreach ($fields_list as $field) {

                    // if requested field exists in the row
                    if (isset($row[trim($field)])) {
                    
                        // put it in the return result
                        $retData[trim($fields)] = $row[trim($field)];
                        
                    }

                }

                // return requested fields
                return $retData;

            // if a specific field was requested and field exists in the row
            } elseif (isset($row[trim($field)])) {

                // return the field's value
                return $row[$field];

            }

        }

        // if error or no results were found
        // return empty string
        return "";

    }

    /**
     *  Looks up the maximum value in a column of a table
     *
     *  Example:
     *
     *  <code>
     *      /**
     *          notice that we're doing no error checking as we will have
     *          any errors show up in the debug window
     *          so don't forget to have at the end of your code a call to
     *          show_debug_info() method
     *      {@*}
     *      $maxAge = $db->dmax("age", "people", "countryid = ?", array($_POST["countryID"]));
     *  </code>
     *
     *  @param  string  $column         Name of the column in which to look
     *
     *  @param  string  $table          Name of the table to use
     *
     *  @param  string  $condition      (optional) A standard MySQL WHERE condition
     *
     *                                  Default is ""
     *
     *  @param  array   $replacements   (optional) An array with as many items as the total "?" symbols in $coumn, $table and
     *                                  $condition. Each item will be automatically {@link escape()}-ed and will replace
     *                                  the corresponding "?"
     *
     *                                  Default is ""
     *
     *  @param  mixed   $cache          (optional) Instructs the script on whether it should cache the query's results. This argument
     *                                  can be either FALSE - meaning no cache - or an integer representing the number of seconds after
     *                                  which the cached results are considered to be expired
     *
     *                                  Default is FALSE
     *
     *  @param  boolean $highlight      (optional) Whether or not the query should be highlighted in the debugger window
     *
     *                                  Default is FALSE
     *
     *                                  This argument is available since version 1.0.7
     *
     *  @return mixed   The maximum found value or an empty string if no values were found
     */
    function dmax($column, $table, $condition = "", $replacements = "", $cache = false, $highlight = false)
    {

        // get the backtrace info so that the debug information will be about where this particular method was called from
        $this->backtraceInfo = debug_backtrace();

        // executes query
        $this->lastResult = $this->query("
        
            SELECT
                MAX(" . $column . ") AS maxval
            FROM
                " . $table .
                
            ($condition != "" ? " WHERE " . $condition : "")

        ,$replacements, $cache, $highlight);

        // if query was executed successfully and one or more results were found
        if ($this->lastResult && $this->foundRows > 0) {

            // get all the data in the row
            $row = $this->fetch_assoc();

            // return the result
            return $row["maxval"];
            
        }

        // if error or no results were found
        return "";

    }

    /**
     *  Sums the values in a column of a table
     *
     *  Example:
     *
     *  <code>
     *      /**
     *          notice that we're doing no error checking as we will have
     *          any errors show up in the debug window
     *          so don't forget to have at the end of your code a call to
     *          show_debug_info() method
     *      {@*}
     *      $sumSalary = $db->dsum("salary", "people", "countryid = ?", array($_POST["countryID"]));
     *  </code>
     *
     *  @param  string  $column         Name of the column in which to look
     *
     *  @param  string  $table          Name of the table to use
     *
     *  @param  string  $condition      (optional) A standard MySQL WHERE condition
     *
     *                                  Default is ""
     *
     *  @param  array   $replacements   (optional) An array with as many items as the total "?" symbols in $column, $table and
     *                                  $condition. Each item will be automatically {@link escape()}-ed and will replace
     *                                  the corresponding "?"
     *
     *                                  Default is ""
     *
     *  @param  mixed   $cache          (optional) Instructs the script on whether it should cache the query's results. This argument
     *                                  can be either FALSE - meaning no cache - or an integer representing the number of seconds after
     *                                  which the cached results are considered to be expired
     *
     *                                  Default is FALSE
     *
     *  @param  boolean $highlight      (optional) Whether or not the query should be highlighted in the debugger window
     *
     *                                  Default is FALSE
     *
     *                                  This argument is available since version 1.0.7
     *
     *  @return mixed   The sum of the values
     */
    function dsum($column, $table, $condition = "", $replacements = "", $cache = false, $highlight = false)
    {

        // get the backtrace info so that the debug information will be about where this particular method was called from
        $this->backtraceInfo = debug_backtrace();

        // executes query
        $this->lastResult = $this->query("
        
            SELECT
                SUM(" . $column . ") as sumval
            FROM
                ". $table .
                
            ($condition != "" ? " WHERE " . $condition : "")

        ,$replacements, $cache, $highlight);

        // if query was executed successfully and one or more results were found
        if ($this->lastResult && $this->foundRows > 0) {

            // get all the data in the row
            $row = $this->fetch_assoc();

            // return the result
            return $row["sumval"];

        }

        // if error or no results were found
        return 0;

    }
    
    /**
     *  Escapes a string's in order to prevent MySQL injections
     *  Works even if magic_quotes is ON
     *
     *  Example:
     *
     *  <code>
     *      print_r($db->escape("John O'Bryan"));
     *  </code>
     *
     *  @param  string  $string     String to escape
     *
     *  @since  1.0.9
     *
     *  @return string  Escaped string
     */
    function escape($string)
    {

        // checks is there is an active connection
        if ($this->_connected()) {

            // get the state of "magic quotes"
            // and if "magic quotes" are on
            if (get_magic_quotes_gpc()) {

                // strip slashes
                $string = stripslashes($string);

            }

            // escape the string
            $returnValue = mysql_real_escape_string($string, $this->link);

            // return escaped string
            return $returnValue;
            
        }

        // upon error we don't have to report anything as _connected() method already did
        // just return false
        return false;

    }

    /**
     *  Alias of {@link escape()} method
     */
    function escape_string($string)
    {

        return $this->escape($string);
        
    }
    
    /**
     *  Alias of the mysql_fetch_assoc() function.
     *
     *  Returns an associative array that corresponds to the fetched row and moves the internal data pointer ahead
     *
     *  Example:
     *
     *  <code>
     *      $result = $db->query("SELECT * FROM table WHERE 1");
     *      while ($row = $db->fetch_assoc($result)) {
     *          // do stuff...
     *      }
     *  </code>
     *
     *  @param  resource    $resource   (optional) Resource to fetch.
     *
     *                                  <i>If not specified, the last result resource returned by the {@link query()} method is used</i>
     *
     *                                  <b>Please be aware that not specifying a resource will result in unpredictable behaviour if
     *                                  you are using nested fetch_assoc's!</b>
     *
     *  @return mixed       Returns an associative array that corresponds to the fetched row, or FALSE if there are no more rows
     */
    function fetch_assoc($resource = "")
    {

        // checks is there is an active connection
        if ($this->_connected()) {

            // if no resource was specified, and there was a previous call to the "query" method
            if ($resource == "" && isset($this->lastResult)) {
            
                // assign the last resource
                $resource = & $this->lastResult;
                
            }

            // check if given resource is valid
            if (is_resource($resource)) {

                // return the fetched row
                return mysql_fetch_assoc($resource);
                
            // if $resource is actually an array taken from cache
            } elseif (is_array($resource)) {

                // get the next entry in the array
                $result = each($resource);

                // return as an associative array
                return $result[1];

            // if not a valid resource
            } else {

                // save debug information
                $this->saveDebugInfo(

                    "messages",

                    array(

                        "message"   =>  "strLang_notAValidResource",
                        "method"    =>  "fetch_assoc()"

                    ),

                    true

                );

            }

        }
        
        // we don't have to report any error as _connected() method already did or checking for valid resource failed
        return false;

    }

    /**
     *  Alias of the mysql_fetch_object() function.
     *
     *  Returns an object with properties that correspond to the fetched row and moves the internal data pointer ahead
     *
     *  Example:
     *
     *  <code>
     *      $result = $db->query("SELECT * FROM table WHERE 1");
     *      while ($row = $db->fetch_obj($result)) {
     *          // do stuff...
     *      }
     *  </code>
     *
     *  @param  resource    $resource   (optional) Resource to fetch.
     *
     *                                  <i>If not specified, the last result resource returned by the {@link query()} method is used</i>
     *
     *                                  <b>Please be aware that not specifying a resource will result in unpredictable behaviour if
     *                                  you are using nested fetch_assoc's!</b>
     *
     *  since 1.0.8
     *
     *  @return mixed       Returns an object with properties that correspond to the fetched row, or FALSE if there are no more rows
     */
    function fetch_obj($resource = "")
    {

        // checks is there is an active connection
        if ($this->_connected()) {

            // if no resource was specified, and there was a previous call to the "query" method
            if ($resource == "" && isset($this->lastResult)) {
            
                // assign the last resource
                $resource = & $this->lastResult;
                
            }

            // check if given resource is valid
            if (is_resource($resource)) {

                // return the fetched row
                return mysql_fetch_object($resource);

            // if $resource is actually an array taken from cache
            } elseif (is_array($resource)) {

                // get the next entry in the array
                $result = each($resource);
                
                // if we're not past the end of the array
                if ($result !== false) {

                    // create a new generic object -> similar with $obj = new stdClass() but i like this one better ;)
                    $obj = (object) NULL;
                    
                    // populate the objects properties'
                    foreach ($result[1] as $key=>$value) {

                        $obj->$key = $value;

                    }

                // if we're past the end of the array
                } else {

                    // make sure we return false
                    $obj = false;

                }
                
                // return as object
                return $obj;

            // if not a valid resource
            } else {

                // save debug information
                $this->saveDebugInfo(

                    "messages",

                    array(

                        "message"   =>  "strLang_notAValidResource",
                        "method"    =>  "fetch_obj()"

                    ),

                    true

                );

            }

        }

        // we don't have to report any error as _connected() method already did or checking for valid resource failed
        return false;

    }

    /**
     *  Stops the execution of the script at the line where this method is called
     *  and, if the {@link debug} property is set to TRUE and the viewer's IP address
     *  it's in the {@link debuggerIP} array, outputs the debug window
     *
     *  @return void
     */
    function halt()
    {

        $this->show_debug_info();

        die();

    }
    
    /**
     *  Shorthand for INSERT queries
     *
     *  <i>I recommend the use of the {@link query()} method instead and writing the actual query for better code readability</i>
     *
     *  <i>The advantage of using this method is that will escape all field names allowing for reserved words to be used</i>
     *
     *  @param  string  $table          Table in which to insert
     *
     *  @param  string  $fields         An array of fields to which to insert data
     *
     *  @param  array   $values         An array with as many items as $fields, containing the data to be inserted into the coresponding
     *                                  fields specified by $fields. Don't worry about escaping strings, these will be automatically
     *                                  {@link escape()}-ed
     *
     *  @param  boolean $highlight      (optional) Whether or not the query should be highlighted in the debugger window
     *
     *                                  Default is FALSE
     *
     *  @since  1.0.9
     *
     *  @return boolean     Returns TRUE on success of FALSE on error
     */
    function insert($table, $fields, $values, $highlight = false)
    {

        // get the backtrace info so that the debug information will be about where this particular method was called from
        $this->backtraceInfo = debug_backtrace();
        
        $str = "";
        
        $rep = "";
        
        for ($i = 0; $i < count($fields); $i++) {

            $str .= ($str != "" ? ", " : "") . "`" . $fields[$i] . "`";
            
            $rep .= ($rep != "" ? ", " : "") . "?";

        }

        // executes query
        // and yes, we mute the errors and let the query() method to handle errors
        $this->lastResult = $this->query("

            INSERT INTO
                " . $table . "
                (" . $str . ")
            VALUES
                (" . $rep . ")"

        ,$values, false, $highlight);

        // if query was executed successfully
        if ($this->lastResult) {

            // returns true
            return true;

        }

        // return false on error
        return false;

    }

    /**
     *  Alias of the the mysql_insert_id() function.
     *
     *  Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query
     *
     *  @since 1.0.5
     *
     *  @return mixed   The ID generated for an AUTO_INCREMENT column by the previous INSERT query on success,
     *                  '0' if the previous query does not generate an AUTO_INCREMENT value, or FALSE if there was
     *                  no MySQL connection
     */
    function insert_id()
    {

        // checks is there is an active connection
        if ($this->_connected()) {

            // if there was a previous call to the "query" method
            if (isset($this->lastResult)) {

                // return the fetched row
                return mysql_insert_id($this->link);

            // if no previous calls to the "query" method
            } else {

                // save debug information
                $this->saveDebugInfo(

                    "messages",

                    array(

                        "message"   =>  "strLang_notAValidResource",
                        "method"    =>  "insert_id()"

                    ),

                    true

                );

            }

        }

        // we don't have to report any error as _connected() method already did or checking for valid resource failed
        return false;

    }

    /**
     *  Parses a MySQL dump file
     *
     *  (this script was posted on php.net so there are some people who contributed to it:
     *  thomas@pixtur.de and celtic@raven-blue.com - thanks guys!)
     *
     *  <i>If you must parse a very large file and your script crashed due to memory limitations, try and set php's memory limit higher</i>
     *
     *  <code>
     *      ini_set("memory_limit","20M");
     *  </code>
     *
     *  @param  string  $url    Path to the file to be parsed
     *
     *  @return void
     */
    function parse_mysql_dump_file($url)
    {

        // checks is there is an active connection
        if ($this->_connected()) {

            // uncomment the next line if you get an error about the memory limit

            // read file into an array
            $file_content = @file($url);

            // if file was successfully opened
            if ($file_content) {

                $query = "";

                // iterates through every line of the file
                foreach ($file_content as $sql_line) {

                    // trims whitespace from both beginning and end of line
                    $tsql = trim($sql_line);

                    // if line content is not empty and is the line does not represent a comment
                    if ($tsql != "" && substr($tsql, 0, 2) != "--" && substr($tsql, 0, 1) != "#") {

                        // add to query string
                        $query .= $sql_line;

                        // if line ends with ";"
                        if (preg_match("/;\s*\$/", $sql_line)) {

                            // get the backtrace info so that the debug information will be about where this particular method was called from
                            $this->backtraceInfo = debug_backtrace();

                            // executes query
                            $this->lastResult = $this->query($query);

                            // empties the query string
                            $query = "";

                        }

                    }

                }

            } else {

                // save debug info
                $this->saveDebugInfo(

                    "messages",

                    array(

                        "message"   =>  "strLang_fileCouldNotBeOpened",
                        "method"    =>  "parse_mysql_dump_file()"

                    ),

                    true

                );

            }

        }

        // we don't have to report any error as _connected() method already did or checking for file returned FALSE
        return false;

    }

    /**
     *  Alias of the mysql_query() function.
     *
     *  After a SELECT query you can get the number of records that would've been returned if there was no LIMIT, by reading the
     *  {@link foundRows} property (of course, that is, if you a have a LIMIT in your query) and the actual number of returned
     *  records by reading the {@link returnedRows} property
     *
     *  After an UPDATE, INSERT or DELETE query you can get the number of affected rows by reading the {@link affectedRows} property.
     *
     *  <b>Note that you don't need to return the result of this method in a variable for using it later with
     *  {@link fetch_assoc()} or {@link fetch_obj()} methods as both these methods, if called without arguments, work on the LAST returned
     *  result resource!</b>
     *
     *  Example:
     *
     *  <code>
     *      /**
     *          notice that we're doing no error checking as we will have
     *          any errors show up in the debug window
     *          so don't forget to have at the end of your code a call to
     *          show_debug_info() method
     *      {@*}
     *
     *      $result = $db->query("SELECT * FROM table WHERE 1");
     *
     *      $result = $db->query("SELECT * FROM table WHERE name = ?", array("Mikey Mouse"));
     *  </code>
     *
     *  @param  string  $sql            SQL to execute
     *
     *  @param  array   $replacements   (optional) An array with as many items as the total "?" symbols in $field, $table and
     *                                  $condition. Each item will be automatically {@link escape()}-ed and will replace
     *                                  the corresponding "?"
     *
     *                                  Default is ""
     *
     *  @param  mixed   $cache          (optional) Instructs the script on whether it should cache the query's results. This argument
     *                                  can be either FALSE - meaning no cache - or an integer representing the number of seconds after
     *                                  which the cached results are considered to be expired
     *
     *                                  Default is FALSE
     *
     *  @param  boolean $highlight      (optional) Whether or not the query should be highlighted in the debugger window
     *
     *                                  Default is ""
     *
     *                                  This argument is available since version 1.0.7
     *
     *  @return mixed        On success, returns a resource or an array (if results are taken from the cache) or FALSE on error
     *
     *                      <i>If query results are taken from cache, the returned result will be an array containing the actual results
     *                      of the query! This is important because even though the {@link fetch_assoc()} and {@link fetch_obj()} methods
     *                      of this class can handle it correctly, PHP's functions like mysql_data_seek() will crash your script! The
     *                      idea is that, if taken from the cache, this method returns an array and should be treated accordingly</i>
     *
     */
    function query($sql, $replacements = "", $cache = false, $highlight = false)
    {
    
        // if backtrace info was not previously set by a method of this class
        if (!isset($this->backtraceInfo)) {

            // get the backtrace info
            $this->backtraceInfo = debug_backtrace();
            
        }
        
        // get some variables from the back trace array
        $callerFile = $this->backtraceInfo[0]["file"];

        $callerLine = $this->backtraceInfo[0]["line"];

        $callerMethod = $this->backtraceInfo[0]["function"];

        // unset the backtrace array
        unset($this->backtraceInfo);

        // checks is there is an active connection
        if ($this->_connected()) {
        
            unset($this->affectedRows);

            // if $replacements is specified but it's not an array
            if ($replacements != "" && !is_array($replacements)) {

                // if debugging is on
                if ($this->debug) {

                    // save debug information
                    $this->saveDebugInfo(

                        "queries",

                        array(

                            "query"     =>  $sql,
                            "file"      =>  $callerFile,
                            "line"      =>  $callerLine,
                            "method"    =>  $callerMethod,
                            "error"     =>  $this->languageStrings["strLang_warningReplacementsNotArray"]

                        ),

                        true

                    );

                }

            // if $replacements is specified and is an array
            } elseif ($replacements != "" && is_array($replacements)) {

                // found how many items to replace are there in the query string
                preg_match_all("/\?/", $sql, $matches, PREG_OFFSET_CAPTURE);
                
                // if the number of items to replace is different than the number of items specified in $replacements
                if (count($matches[0]) != count($replacements)) {

                    // if debugging is on
                    if ($this->debug) {
                    
                        // save debug information
                        $this->saveDebugInfo(
                        
                            "queries",
                            
                            array(
                            
                                "query"     => $sql,
                                "file"      => $callerFile,
                                "line"      => $callerLine,
                                "method"    => $callerMethod,
                                "error"     => $this->languageStrings["strLang_warningReplacementsWrongNumber"]
                                
                            ),
                            
                            true
                            
                        );
                        
                    }
                    
                // if the number of items to replace is the same as the number of items specified in $replacements
                } else {
                
                    // make preparations for the replacement
                    $pattern1 = array();

                    $pattern2 = array();

                    // prepare question marks for replacement
                    foreach ($matches[0] as $match) {
                    
                        $pattern1[] = "/\\" . $match[0] . "/";
                        
                    }

                    foreach ($replacements as $key=>$replacement) {

                        // generate a string
                        $randomstr = md5(microtime()) . $key;
                        
                        // prepare the replacements for the question marks
                        $replacements1[] = $randomstr;

                        // mysql_real_escape_string the items in replacements
                        $replacements2[$key] = "'" . $this->escape_string($replacement) . "'";

                        // and also, prepare the new pattern to be replaced afterwards
                        $pattern2[$key] = "/" . $randomstr . "/";

                    }

                    // replace each question mark with something new
                    // (we do this intermediar step so that we can actually have question marks in the replacements)
                    $sql = preg_replace($pattern1, $replacements1, $sql, 1);
                    
                    // perform the actual replacement
                    $sql = preg_replace($pattern2, $replacements2, $sql, 1);

                }

            }
            
            // if we have a SELECT query and the SQL_CALC_FOUND_ROWS string is not in it
            // (we do this trick to get the numbers of records that would've been returned if there was no LIMIT applied)
            if (strtolower(substr(ltrim($sql), 0, 6)) == "select" && strpos($sql, "SQL_CALC_FOUND_ROWS") === false) {
            
                // add the 'SQL_CALC_FOUND_ROWS' parameter to the query
                $sql = preg_replace("/SELECT/i", "SELECT SQL_CALC_FOUND_ROWS", $sql, 1);

            }
            
            unset($this->lastResult);
            
            // starts a timer
            list($usec, $sec) = explode(" ", microtime());
            $startTime = (float)$usec + (float)$sec;
            
            $refreshedCache = "nocache";

            // if we need to look for a cached version of the query's results
            if ($cache !== false) {
            
                $refreshedCache = false;

                // if cache folder exists and is writable
                if (file_exists($this->cacheFolder) && is_dir($this->cacheFolder) && is_writable($this->cacheFolder)) {
                
                    $fileName = $this->cacheFolder . md5($sql);
                
                    // if a cached version of this query's result already exists and it is not expired
                    if (file_exists($fileName) && filemtime($fileName) + $cache > mktime()) {
                    
                        $this->lastResult = unserialize(file_get_contents($fileName));
                        
                        reset($this->lastResult);

                    }

                // if folder doesn't exists and debugging is on
                } elseif ($this->debug) {

                    // save debug information
                    $this->saveDebugInfo(

                        "messages",

                        array(

                            "message"   =>  "strLang_cacheFolderIsNotWritable",
                            "method"    =>  "query()"

                        ),

                        true

                    );

                }

            }

            // if query was not read from the cache
            if (!isset($this->lastResult)) {

                // executes the query
                $this->lastResult = @mysql_query($sql, $this->link);

            }

            // stops timer
            list($usec, $sec) = explode(" ", microtime());
            $endTime = (float)$usec + (float)$sec;
            
            // if execution time exceeds maxQueryTime
            if ($endTime - $startTime > $this->maxQueryTime) {
            
                // then send a notification mail
                @mail(
                    $this->maxQueryTimeExceeded_notificationAddress,
                    sprintf($this->languageStrings["strLang_erroneousQueryEMailSubject"], $this->maxQueryTimeExceeded_notifierDomain),
                    sprintf($this->languageStrings["strLang_erroneousQueryEMailContent"], $this->maxQueryTime, $endTime - $startTime, $sql),
                    "From: " . $this->maxQueryTimeExceeded_notifierDomain
                );
                
            }

            // if the query was successfully executed
            if ($this->lastResult) {
            
                // if query's result was not read from cache (meaning $this->lastResult is a result resource or boolean TRUE - as queries
                // like UPDATE, DELETE, DROP return boolean true on success rather than a result resource)
                if (is_resource($this->lastResult) || $this->lastResult === true) {
                
                    // check if it is a SELECT query
                    if (preg_match("/^SELECT/i", trim($sql)) > 0) {

                        $isSelectQuery = true;

                    // if not a SELECT query
                    } else {

                        $isSelectQuery = false;

                    }

                    // query's results need to be cached
                    if ($cache !== false) {

                        $refreshedCache = true;

                        $cacheData = array();

                        // and save the results in a temporary variable
                        while ($row = mysql_fetch_assoc($this->lastResult)) {

                            $cacheData[] = $row;

                        }

                        // resets the internal pointer of the result resource
                        // we have to mute error reporting because if the result set is empty (mysql_num_rows() == 0),
                        // a seek to 0 will fail with a E_WARNING!
                        @mysql_data_seek($this->lastResult, 0);

                        // deletes (if exists) the previous cache file
                        @unlink($fileName);

                        // creates the new cache file
                        $handle = fopen($fileName, "wb");

                        $this->lastResult = $cacheData;

                        // saves the query's result in it
                        fwrite($handle, serialize($this->lastResult));

                        // and close the file
                        fclose($handle);

                    }

                    if ($isSelectQuery) {

                        // the returnedRows property holds the number of records returned by a SELECT query
                        $this->returnedRows = @mysql_num_rows($this->lastResult);

                        // get the number of records that would've been returned if there was no LIMIT
                        $foundRows = mysql_fetch_assoc(mysql_query("SELECT FOUND_ROWS()", $this->link));

                        $this->foundRows = $foundRows["FOUND_ROWS()"];

                    } else {

                        // the affectedRows property holds the number of affected rows by action queries (DELETE, INSERT, UPDATE)
                        $this->affectedRows = @mysql_affected_rows($this->link);

                    }

                // if query was read from cache
                } else {
                
                    // if read from cache this must be a SELECT query
                    $isSelectQuery = true;

                    // get the number of records
                    $this->returnedRows = $this->foundRows = count($this->lastResult);

                }

                // if debugging is on
                if ($this->debug) {
                
                    $warning = "";

                    $result = array();
                    
                    // if rows were returned
                    if ($isSelectQuery) {
                    
                        $rowCounter = 0;

                        // put the first rows, as defined by showMaxRows, in an array
                        // if query was not read from cache
                        if (is_resource($this->lastResult)) {
                        
                            while ($row = mysql_fetch_assoc($this->lastResult)) {

                                if ($rowCounter++ < $this->showMaxRows) {

                                    $result[] = $row;

                                }

                            }

                            // reset the pointer in the result afterwards
                            // we have to mute error reporting because if the result set is empty (mysql_num_rows() == 0),
                            // a seek to 0 will fail with a E_WARNING!
                            @mysql_data_seek($this->lastResult, 0);

                        // if query was read from the cache
                        } else {
                        
                            foreach ($this->lastResult as $row) {

                                if ($rowCounter++ < $this->showMaxRows) {

                                    $result[] = $row;

                                }

                            }
                            
                            reset($this->lastResult);

                        }
                        
                        // if there were queries run already
                        if (isset($this->debugInfo["queries"])) {

                            // iterate through the run queries
                            // to find out if this query was already run
                            $counter = 1;

                            $keys = array();

                            foreach ($this->debugInfo["queries"] as $key=>$queryData) {
                            
                                // if this query was run before
                                if (isset($queryData["result"]) && $queryData["result"] == $result) {

                                    // increase the counter
                                    $counter++;

                                    // save the pointer to the query in an array
                                    $keys[] = $key;

                                }

                            }

                            // if the query was run before
                            if ($counter > 0) {

                                // issue a warning for all the queries that were found to be the same as the current one
                                foreach ($keys as $key) {

                                    $warning = sprintf($this->languageStrings["strLang_optimizationNeeded"], $counter);

                                    $this->debugInfo["queries"][$key]["warning"] = $warning;

                                }

                            }

                        }

                        // if it's a SELECT query
                        if ($isSelectQuery && is_resource($this->lastResult)) {
                        
                            // ask the MySQL to EXPLAIN the query
                            $explainResource = mysql_query("EXPLAIN EXTENDED " . $sql);

                            // if query returned a result
                            // (as some queries cannot be EXPLAIN-ed like SHOW TABLE, DESCRIBE, etc)
                            if ($explainResource) {

                                // put all the records returned by the explain query in an array
                                while ($row = mysql_fetch_assoc($explainResource)) {

                                    $explain[] = $row;

                                }

                            }
                            
                        }

                    }
                    
                    // save debug information
                    $this->saveDebugInfo(
                    
                        "queries",
                        
                        array(
                        
                            "query"         =>  $sql,
                            "result"        =>  $result,
                            "returnedRows"  =>  $this->returnedRows,
                            "explain"       =>  (isset($explain) ? $explain : ""),
                            "affectedRows"  =>  (isset($this->affectedRows) ? $this->affectedRows : ""),
                            "file"          =>  $callerFile,
                            "line"          =>  $callerLine,
                            "method"        =>  $callerMethod,
                            "executionTime" =>  $endTime - $startTime,
                            "warning"       =>  $warning,
                            "highlight"     =>  $highlight,
                            "fromCache"     =>  $refreshedCache

                        )
                        
                    );
                    
                }
                
                // return result resource
                return $this->lastResult;
                
            }
            
            // in case of error and if debugging is on
            if ($this->debug) {
            
                // save debug information
                $this->saveDebugInfo(
                
                    "queries",
                    
                    array(
                    
                        "query"     =>  $sql,
                        "file"      =>  $callerFile,
                        "line"      =>  $callerLine,
                        "method"    =>  $callerMethod,
                        "error"     =>  mysql_error($this->link)
                        
                    ),
                    
                    true
                    
                );
                
            }

        }
        
        // we don't have to report any error as _connected() method already did or any of the previous checks
        return false;
        
    }
    
    /**
     *  Handles saving of debug information and halts the execution of the script on fatal error or if the {@link haltOnErrors} property
     *  is set to TRUE
     *
     *  @access private
     */
    function saveDebugInfo($category, $infoArray, $fatal = false)
    {

        // saves debug information
        $this->debugInfo[$category][] = $infoArray;

        // if the saved debug info is about a fatal error
        // and execution is to be stopped on fatal errors
        if ($this->debug && $fatal && $this->haltOnErrors) {

            // show the debugging window
            $this->show_debug_info();

            // halt execution
            die();

        }

    }

    /**
     *  Sets a new language in which to display texts in the debug window
     *
     *  @param  string  $language   The name of the php language file you wish to use from the /languages folder must be specified
     *                              Without the extension! (i.e. "german" for the german language not "german.php")
     *
     *  @var   string
     *
     *  @return void
     */
    function setLanguage($language)
    {

        // if language is not already set
        if ($this->language != $language) {

            // set the current language
            $this->language = $language;

            // include the language file
            require_once $this->classAbsolutePath . "/languages/" . $this->language . ".php";

        }

    }

    /**
     *  Outputs debug information if the {@link debug} property is set to TRUE and the viewer's IP address is in the range specified
     *  by the {@link debuggerIP} property
     *
     *  @return void
     */
    function show_debug_info()
    {

        // if debug is enabled
        if (

            $this->debug &&
            is_array($this->debuggerIP) &&

                (
                    empty($this->debuggerIP) ||
                    in_array($_SERVER["REMOTE_ADDR"], $this->debuggerIP)

                )

        ) {
        
            // includes, if not included, the xtemplate class
            if (!class_exists("XTemplate")) {
            
                require_once $this->classAbsolutePath . "/includes/class.xtemplate.php";
                
            }
            
            $xtpl = new XTemplate($this->classAbsolutePath . "/templates/" . $this->template . "/debug.xtpl");
            
            $xtpl->assign("templatePath", $this->classRelativePath . "/templates/" . $this->template . "/");
            
            // assign all the values from the language file
            $xtpl->assign("languageStrings", $this->languageStrings);
            
            $totalExecutionTime = 0;

            $successfulQueries = 0;

            $unsuccessfulQueries = 0;
            
            // if there is data about queries
            if (isset($this->debugInfo["queries"])) {
            
                // marks symbols in MySQL query
                $symbols = array(
                    "/(\=)/",
                    "/(\>)/",
                    "/(\<)/",
                    "/(\*)/",
                    "/(\+)/",
                    "/(\-)/",
                    "/(\,)/",
                    "/(\.)/",
                    "/(\()/",
                    "/(\))/"
                );

                // marks strings in MySQL queries
                $strings = array(
                    "/\'([^\']*)\'|\"([^\']*)\"/",
                );

                // marks operations in MySQL queries
                $operations = array(
                    "/(\bDELETE\b)/i",
                    "/(\bEXPLAIN\b)/i",
                    "/(\bFROM\b)/i",
                    "/(\bGROUP BY\b)/i",
                    "/(\bHAVING\b)/i",
                    "/(\bINNER JOIN\b)/i",
                    "/(\bINSERT INTO\b)/i",
                    "/(\bLEFT JOIN\b)/i",
                    "/(\bLIMIT\b)/i",
                    "/(\bORDER BY\b)/i",
                    "/(\bREPLACE INTO\b)/i",
                    "/(\bRIGHT JOIN\b)/i",
                    "/(\bSELECT\b)/i",
                    "/(\bSET\b)/i",
                    "/(\bUPDATE\b)/i",
                    "/(\bUNION\b)/i",
                    "/(\bVALUES\b)/i",
                    "/(\bWHERE\b)/i"
                );

                // marks other special entities in MySQL queries
                $special = array(
                    "/(\bAND\b)/i",
                    "/(\bOR\b)/i",
                    "/(\bDESC\b)/i",
                    "/(\bASC\b)/i",
                    "/(\bIN\b)/i",
                    "/(\bIS\b)/i",
                    "/(\bNOT\b)/i",
                    "/(\bNULL\b)/i",
                    "/(\bAS\b)/i",
                    "/(\bON\b)/i"
                );

                // total number of the queries
                $xtpl->assign("totalQueries", count($this->debugInfo["queries"]));
                
                // iterate through all the query related data
                foreach ($this->debugInfo["queries"] as $debugInfo) {

                    $replacement = htmlentities("<span class=\"databasedebugMySQLsymbols\">\$1</span>");

                    $debugInfo["query"] = preg_replace($symbols, $replacement, $debugInfo["query"]);

                    $replacement = htmlentities("'<span class=\"databasedebugMySQLstrings\">\$1</span>'");

                    $debugInfo["query"] = preg_replace($strings, $replacement, $debugInfo["query"]);

                    $replacement = htmlentities("<br /><span class=\"databasedebugMySQLoperations\">\$1</span><br /><span style='padding-left:20px'></span>");

                    $debugInfo["query"] = preg_replace($operations, $replacement, $debugInfo["query"]);

                    $replacement = htmlentities("<span class=\"databasedebugMySQLspecial\">\$1</span>");

                    $debugInfo["query"] = preg_replace($special, $replacement, $debugInfo["query"]);

                    // assign the query string
                    $xtpl->assign("query", html_entity_decode($debugInfo["query"]));
                    
                    // if there is no error message
                    if (!isset($debugInfo["error"])) {
                    
                        $queryResult = "successful";

                        // assign the execution time
                        $xtpl->assign("executionTime", $debugInfo["executionTime"]);

                        // increment total execution time of queries
                        $totalExecutionTime += $debugInfo["executionTime"];

                    } else {

                        $queryResult = "unsuccessful";

                        // assign the error to report
                        $xtpl->assign("error", $debugInfo["error"]);

                    }
                    
                    // increment the counter of queries with current query's result
                    ${$queryResult . "Queries"}++;
                        
                    $xtpl->assign("nr", ${$queryResult . "Queries"});

                    // if there is information about the file the query was called from
                    if ($debugInfo["file"] != "") {

                        $xtpl->assign("file", $debugInfo["file"]);

                        $xtpl->parse("main." . $queryResult . "QueriesContainer." . $queryResult . "QueriesEntry.extraInfo.callerFile");

                    // if no information about the file the query was called from
                    } else {

                        $xtpl->parse("main." . $queryResult . "QueriesContainer." . $queryResult . "QueriesEntry.extraInfo.callerFile_notSpecified");

                    }
                        
                    // if there is information about the line the query was called from
                    if ($debugInfo["line"] != "") {

                        $xtpl->assign("line", $debugInfo["line"]);

                        $xtpl->parse("main." . $queryResult . "QueriesContainer." . $queryResult . "QueriesEntry.extraInfo.callerLine");

                    // if no informations about the line the query was called from
                    } else {

                        $xtpl->parse("main." . $queryResult . "QueriesContainer." . $queryResult . "QueriesEntry.extraInfo.callerLine_notSpecified");

                    }

                    // if there is information about the method the query was called by
                    if ($debugInfo["method"] != "") {
                    
                        $xtpl->assign("callerMethod", $debugInfo["method"]);

                        $xtpl->parse("main." . $queryResult . "QueriesContainer." . $queryResult . "QueriesEntry.extraInfo.callerMethod");
                        
                    }
                        
                    // parse extra info
                    $xtpl->parse("main." . $queryResult . "QueriesContainer." . $queryResult . "QueriesEntry.extraInfo");
                    
                    // if a successful query
                    if ($queryResult == "successful") {
                    
                        // if query was a successful SELECT query
                        if ($debugInfo["affectedRows"] == "") {
                        
                            $xtpl->assign("returnedRows", !$debugInfo["returnedRows"] ? "0" : $debugInfo["returnedRows"]);

                            // if any rows were returned by the select query
                            if (!empty($debugInfo["result"])) {
                            
                                // display returned rows but
                                // first, parse the names of the fields
                                foreach (array_keys($debugInfo["result"][0]) as $fieldName) {

                                    // prepare to display
                                    $xtpl->assign("class", "database-debug-records-tableHeader");

                                    $xtpl->assign("caption", $fieldName);

                                    // parse field
                                    $xtpl->parse("main.successfulQueriesContainer.successfulQueriesEntry.selectQuery_related.records.row.cell");

                                }

                                // wrap up displaying the fields names
                                $xtpl->parse("main.successfulQueriesContainer.successfulQueriesEntry.selectQuery_related.records.row");

                                // iterate through all the returned rows
                                foreach ($debugInfo["result"] as $key=>$row) {

                                    // iterate through row's fields
                                    foreach ($row as $field) {

                                        // prepare each field for displaying
                                        $xtpl->assign("class", "database-debug-records-tableField");

                                        $xtpl->assign("caption", htmlentities($field));

                                        // parse field
                                        $xtpl->parse("main.successfulQueriesContainer.successfulQueriesEntry.selectQuery_related.records.row.cell");

                                    }

                                    // wrap up displaying the row
                                    $xtpl->parse("main.successfulQueriesContainer.successfulQueriesEntry.selectQuery_related.records.row");

                                }

                                // wrap up displaying of the returned rows
                                $xtpl->parse("main.successfulQueriesContainer.successfulQueriesEntry.selectQuery_related.records");

                            }

                            // show EXPLAIN
                            if (!empty($debugInfo["explain"])) {
                            
                                // display returned rows but
                                // first, parse the names of the fields
                                foreach (array_keys($debugInfo["explain"][0]) as $fieldName) {

                                    // prepare to display
                                    $xtpl->assign("class", "database-debug-records-tableHeader");

                                    $xtpl->assign("caption", $fieldName);

                                    // parse field
                                    $xtpl->parse("main.successfulQueriesContainer.successfulQueriesEntry.selectQuery_related.explain.row.cell");

                                }

                                // wrap up displaying the fields names
                                $xtpl->parse("main.successfulQueriesContainer.successfulQueriesEntry.selectQuery_related.explain.row");

                                // iterate through all the returned rows
                                foreach ($debugInfo["explain"] as $key=>$row) {

                                    // iterate through row's fields
                                    foreach ($row as $field) {

                                        // prepare each field for displaying
                                        $xtpl->assign("class", "database-debug-records-tableField");

                                        $xtpl->assign("caption", htmlentities($field));

                                        // parse field
                                        $xtpl->parse("main.successfulQueriesContainer.successfulQueriesEntry.selectQuery_related.explain.row.cell");

                                    }

                                    // wrap up displaying the row
                                    $xtpl->parse("main.successfulQueriesContainer.successfulQueriesEntry.selectQuery_related.explain.row");

                                }

                                // wrap up displaying of the returned rows
                                $xtpl->parse("main.successfulQueriesContainer.successfulQueriesEntry.selectQuery_related.explain");

                                $xtpl->parse("main.successfulQueriesContainer.successfulQueriesEntry.selectQuery_related.show_explain");

                            }
                            
                            // wrap up displaying SELECT query related part
                            $xtpl->parse("main." . $queryResult . "QueriesContainer." . $queryResult . "QueriesEntry.selectQuery_related");

                        // if query was an action query
                        } else {
                        
                            $xtpl->assign("affectedRows", !$debugInfo["affectedRows"] ? "0" : $debugInfo["affectedRows"]);

                            $xtpl->parse("main." . $queryResult . "QueriesContainer." . $queryResult . "QueriesEntry.actionQuery_related");

                        }
                        
                    }
                        
                    // if query has warnings
                    if (isset($debugInfo["warning"]) && $debugInfo["warning"] != "") {
                    
                        // use the designated stylesheet class
                        $xtpl->assign("extend", "-withWarning");

                        $xtpl->assign("warning", $debugInfo["warning"]);
                        
                        // show the warning message
                        $xtpl->parse("main." . $queryResult . "QueriesContainer." . $queryResult . "QueriesEntry.warning");

                    // if query is to be highlighted
                    } elseif (isset($debugInfo["highlight"]) && $debugInfo["highlight"]) {
                    
                        // use the designated stylesheet class
                        $xtpl->assign("extend", "-withHighlight");

                    // use regular stylesheet class
                    } else {

                        $xtpl->assign("extend", "");

                    }
                    
                    if (isset($debugInfo["fromCache"]) && $debugInfo["fromCache"] === false) {
                    
                        $xtpl->parse("main." . $queryResult . "QueriesContainer." . $queryResult . "QueriesEntry.from_cache");

                    }

                    // wrap up displaying of entry
                    $xtpl->parse("main." . $queryResult . "QueriesContainer." . $queryResult . "QueriesEntry");
                    
                }

            }
            
            // if there are no successful queries
            if ($successfulQueries == 0) {
            
                // parse the related template
                $xtpl->parse("main.successfulQueriesContainer.successfulQueriesContainer_Empty");
                
            }
            
            // if there are no unsuccessful queries
            if ($unsuccessfulQueries == 0) {
            
                // parse the related templates
                $xtpl->parse("main.unsuccessfulQueriesContainer.unsuccessfulQueriesContainer_Empty");

                $xtpl->parse("main.header.noUnsuccessfulQueries");
                
            // if there were unsuccessful queries
            } else {
            
                // highlight the fact in header
                $xtpl->parse("main.header.unsuccessfulQueries");
                
            }
            
            // parse the container templates for both successful and unsuccessful queries
            $xtpl->parse("main.unsuccessfulQueriesContainer");

            $xtpl->parse("main.successfulQueriesContainer");
            
            $xtpl->assign("totalExecutionTime", $totalExecutionTime);

            $xtpl->assign("successfulQueries", $successfulQueries);

            $xtpl->assign("unsuccessfulQueries", $unsuccessfulQueries);
            
            $messages = 0;
            
            // if there is data about messages
            if (isset($this->debugInfo["messages"])) {
            
                // highlight the fact in the header
                $xtpl->parse("main.header.messages");

                // iterate through all the messages related data
                foreach ($this->debugInfo["messages"] as $debugInfo) {
                
                    // assign message counter
                    $xtpl->assign("nr", $messages);
                
                    // assign message
                    $xtpl->assign("message", $this->languageStrings[$debugInfo["message"]]);
                    
                    // caller method
                    $xtpl->assign("method", $debugInfo["method"]);
                    
                    // parse the related template
                    $xtpl->parse("main.messagesContainer.messagesEntry");
                    
                    // increment message counter
                    $messages++;
                    
                }
                
            // if there are no messages
            } else {

                $xtpl->parse("main.header.noMessages");

            }
            
            $xtpl->assign("totalMessages", $messages);
            
            // if there are no messages
            if ($messages == 0) {
            
                // parse the related template
                $xtpl->parse("main.messagesContainer.messagesContainer_Empty");
                
            }
            
            // parse the container templates for messages
            $xtpl->parse("main.messagesContainer");
            
            // parse watches
            $watches = 0;

            // if there is data about watches
            if (isset($this->debugInfo["watch"])) {

                // iterate through all the watches related data
                foreach ($this->debugInfo["watch"] as $debugInfo) {

                    // assign output
                    $xtpl->assign("nr", $watches);

                    $xtpl->assign("name", $debugInfo["name"]);

                    $xtpl->assign("watch", print_r($debugInfo["variable"], true));

                    // parse the related template
                    $xtpl->parse("main.watchContainer.watchEntry");

                    // increment watch counter
                    $watches++;

                }

            }

            $xtpl->assign("totalWatches", $watches);

            // if there are no watches
            if ($watches == 0) {
            
                // parse the related template
                $xtpl->parse("main.watchContainer.watchContainer_Empty");
                
            }

            // parse the container templates for watches
            $xtpl->parse("main.watchContainer");

            // parses template for global variables
            $xtpl->assign("globals", print_r($_COOKIE, true));
            $xtpl->assign("varname", "\$_COOKIE");
            $xtpl->assign("section", "cookie");
            $xtpl->parse("main.globalsContainer.globalsEntry");

            $xtpl->assign("globals", print_r($_FILES, true));
            $xtpl->assign("varname", "\$_FILES");
            $xtpl->assign("section", "files");
            $xtpl->parse("main.globalsContainer.globalsEntry");

            $xtpl->assign("globals", print_r($_GET, true));
            $xtpl->assign("varname", "\$_GET");
            $xtpl->assign("section", "get");
            $xtpl->parse("main.globalsContainer.globalsEntry");

            $xtpl->assign("globals", print_r($_POST, true));
            $xtpl->assign("varname", "\$_POST");
            $xtpl->assign("section", "post");
            $xtpl->parse("main.globalsContainer.globalsEntry");
            
            $xtpl->assign("globals", print_r($_SERVER, true));
            $xtpl->assign("varname", "\$_SERVER");
            $xtpl->assign("section", "server");
            $xtpl->parse("main.globalsContainer.globalsEntry");

            $xtpl->assign("globals", @print_r($_SESSION, true));
            $xtpl->assign("varname", "\$_SESSION");
            $xtpl->assign("section", "session");
            $xtpl->parse("main.globalsContainer.globalsEntry");

            $xtpl->parse("main.globalsContainer");
            
            // if the debug bar is to be shown minimized
            if ($this->minimizeDebugger) {
            
                $xtpl->assign("displayMiniBar", "block");

                $xtpl->assign("displayFullBar", "none");
                
            // if the debug bar is to be shown full size
            } else {
            
                $xtpl->assign("displayMiniBar", "none");

                $xtpl->assign("displayFullBar", "block");
                
            }

            // finish the parsing and output the result
            $xtpl->parse("main.header");

            $xtpl->parse("main");

            $xtpl->out("main");
                
        }
        
    }

    /**
     *  Shorthand for emptying queries
     *
     *  <i>Please note that truncating a table is quicker then deleting all rows, as stated in the MySQL documentation at
     *  {@link http://dev.mysql.com/doc/refman/4.1/en/truncate.html}. Truncating a table also resets the value of the
     *  AUTO INCREMENT field</i>
     *
     *  @param  string  $table          Table to empty
     *
     *  @param  boolean $highlight      (optional) Whether or not the query should be highlighted in the debugger window
     *
     *                                  Default is FALSE
     *
     *  @since  1.0.9
     *
     *  @return boolean     Returns TRUE on success of FALSE on error
     */
    function truncate($table, $highlight = false)
    {

        // get the backtrace info so that the debug information will be about where this particular method was called from
        $this->backtraceInfo = debug_backtrace();

        // executes query
        $this->lastResult = $this->query("

            TRUNCATE
                " . $table

        , "", false, $highlight);

        // if query was executed successfully
        if ($this->lastResult) {

            // returns true
            return true;

        }

        // return false on error
        return false;

    }

    /**
     *  Shorthand for UPDATE queries
     *
     *  <i>I recommend the use of the {@link query()} method instead and writing the actual query for better code readability</i>
     *
     *  <i>The advantage of using this method is that will escape all field names allowing for reserved words to be used</i>
     *
     *  @param  string  $table          Table in which to update
     *
     *  @param  string  $fields         An array of fields on which to perform the update
     *
     *  @param  array   $values         An array with as many items as $fields, containing the data new to be inserted into the coresponding
     *                                  fields specified by $fields. Don't worry about escaping strings, these will be automatically
     *                                  {@link escape()}-ed
     *
     *  @param  string  $condition      (optional) A standard MySQL WHERE condition
     *
     *                                  Default is ""
     *
     *  @param  array   $replacements   (optional) An array with as many items as the total "?" symbols in $condition.
     *                                  Each item will be automatically {@link escape()}-ed and will replace the corresponding "?"
     *
     *  @param  boolean $highlight      (optional) Whether or not the query should be highlighted in the debugger window
     *
     *                                  Default is FALSE
     *
     *  @since  1.0.9
     *
     *  @return boolean     Returns TRUE on success of FALSE on error
     */
    function update($table, $fields, $values, $condition = "", $replacements = "", $highlight = false)
    {

        // get the backtrace info so that the debug information will be about where this particular method was called from
        $this->backtraceInfo = debug_backtrace();
        
        $str = "";
        
        for ($i = 0; $i < count($fields); $i++) {

            $str .= ($str != "" ? ", " : "") . "`" . $fields[$i] . "` = ?";

        }

        // executes query
        // and yes, we mute the errors and let the query() method to handle errors
        $this->lastResult = $this->query("

            UPDATE
                " . $table . "
            SET
                " . $str .

            ($condition != "" ? " WHERE " . $condition : "")

        ,array_merge($values, $replacements), false, $highlight);

        // if query was executed successfully
        if ($this->lastResult) {

            // returns true
            return true;

        }

        // return false on error
        return false;

    }

    /**
     *  Saves the state of a variable at a given moment and displays it in the debug window
     *
     *  @param  mixed   $variable       Variable to watch
     *
     *  @param  string  $variableName   (optional) Name to display along the variable's value. Useful when you are watching
     *                                  lots of variables and want to know which one is which
     *
     *  @return void
     */
    function watch($variable, $variableName = "")
    {

        // save debug information
        $this->debugInfo["watch"][] = array(

            "variable"  =>  $variable,
            "name"      =>  $variableName

        );

    }

}
?>
